#pragma once
#include <iostream>
using namespace std;
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <cstring>
#include <functional>
#include <pthread.h>

#define BUFSIZE 1024

enum errnum
{
    SOCK_FAILED = 1,
    BIND_FAILED
};

class udpclient
{
public:
    // 构造
    udpclient(const string &serverIp, uint16_t serverPort)
        : _serverIp(serverIp), _serverPort(serverPort)
    {
    }

    // 析构
    ~udpclient()
    {
    }

    // 初始化
    void init()
    {
        // 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd == -1) // 创建失败
        {
            cerr << "套接字创建失败" << endl;
            exit(SOCK_FAILED);
        }
        // 套接字创建成功
        cerr << "套接字创建成功 sockfd:" << _sockfd << endl;

        // 客户端不需要主动绑定ip地址和端口号，操作系统会自动生产端口号并绑定
    }

    // 接收消息
    static void *receive_message(void *args)
    {
        pthread_detach(pthread_self());                    // 线程分离，当前线程不需要主线程等待回收
        udpclient *_this = static_cast<udpclient *>(args); // this指针

        // 1、构建服务器属性结果
        struct sockaddr_in serverInfor;
        socklen_t len = sizeof(serverInfor);
        bzero(&serverInfor, sizeof(serverInfor));
        serverInfor.sin_family = AF_INET;
        serverInfor.sin_port = htons(_this->_serverPort);
        serverInfor.sin_addr.s_addr = inet_addr(_this->_serverIp.c_str());

        // 2、从网络中读取数据
        char buffer[BUFSIZ];
        while (true)
        {
            int n = recvfrom(_this->_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&serverInfor, &len);
            // 打印翻译结果
            buffer[n] = 0; // 在读取到的数据后面添加\0
            cout << buffer << endl;
        }
        return nullptr;
    }

    // 启动
    void start()
    {
        // 1、创建接收消息的线程
        int n = pthread_create(&_RXpd, nullptr, receive_message, this);
        if (n != 0)
        {
            cerr << "receive线程创建失败" << endl;
            exit(-1);
        }

        // 2、构建服务器属性结果
        struct sockaddr_in serverInfor;
        socklen_t len = sizeof(serverInfor);
        bzero(&serverInfor, sizeof(serverInfor));
        serverInfor.sin_family = AF_INET;
        serverInfor.sin_port = htons(_serverPort);
        serverInfor.sin_addr.s_addr = inet_addr(_serverIp.c_str());

        // 3、向网络中发送数据、以及从网络中读取处理结果
        char sendbuffer[BUFSIZE]; // 暂存要发送的数据
        while (true)
        {
            cerr << "请输入# ";
            fflush(stdout);
            fgets(sendbuffer, sizeof(sendbuffer), stdin); // 从键盘读取一行数据
            int n = strlen(sendbuffer);                   // 读取到的字符个数
            if (n > 0)
            {
                sendbuffer[n - 1] = 0; // 将最后一个换行符抹去
                sendto(_sockfd, sendbuffer, n - 1, 0, (struct sockaddr *)&serverInfor, sizeof(serverInfor));
            }
        }
    }

private:
    int _sockfd;          // 套接字id
    string _serverIp;     // 服务器ip
    uint16_t _serverPort; // 服务器端口号
    pthread_t _RXpd;      // 从网络中读取数据的线程id
};